/*
*
* Copyright (C) 2004 - 2008 BitLaboratory (see http://www.bitlaboratory.com)
* All rights reserved. 
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission. 
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
* OF SUCH DAMAGE.
*
* You can find the latest version of this library at http://www.bitlaboratory.com
*
*/


using System;
using System.Drawing;
using System.Collections;
using System.Threading;
using System.ComponentModel;
using Microsoft.Win32;
using System.Runtime.InteropServices;

namespace BitLaboratory.Win32Api.Themes
{
	/// <summary>
	/// 
	/// </summary>
	public class ThemeRenderer
	{
		#region ThemeHandle

		private class ThemeHandle : IDisposable
		{
			private IntPtr _hTheme;

			private ThemeHandle(IntPtr hTheme)
			{
				this._hTheme = IntPtr.Zero;
				this._hTheme = hTheme;
			}

			~ThemeHandle()
			{
				this.Dispose();
			}

			public static ThemeHandle Create(string className, bool throwExceptionOnFail)
			{
				IntPtr themaHandle = IntPtr.Zero;

				try
				{
					themaHandle = APITheme.OpenThemeData(IntPtr.Zero, className);
				}
				catch( Exception ex )
				{
					if( ThemeRenderer.IsCriticalException(ex) )
					{
						throw;
					}
					if( throwExceptionOnFail )
					{
						throw new InvalidOperationException("Theme Handle Creation Failed!", ex);
					}
					return null;
				}

				if( themaHandle == IntPtr.Zero )
				{
					if( throwExceptionOnFail )
					{
						throw new InvalidOperationException( "Theme Handle Creation Failed" );
					}
					return null;
				}
				return( new ThemeHandle(themaHandle) );
			}

			public void Dispose()
			{
				if( this._hTheme != IntPtr.Zero )
				{
					APITheme.CloseThemeData(this._hTheme);
					this._hTheme = IntPtr.Zero;
				}
			}

			public IntPtr NativeHandle
			{
				get{ return this._hTheme; }
			}
		}

		#endregion

		private string _class;
		private int _part;
		private int _state;
		
		internal const int EdgeAdjust = 8192;
		
		private static long globalCacheVersion;
		private static int numberOfPossibleClasses;
		[ThreadStatic]
		private static Hashtable themeHandles;
		[ThreadStatic]
		private static long threadCacheVersion;

		
		
		static ThemeRenderer()
		{
			ThemeRenderer.numberOfPossibleClasses = 25;
			ThemeRenderer.themeHandles = null;
			ThemeRenderer.threadCacheVersion = ((long)0);
			ThemeRenderer.globalCacheVersion = ((long)0);
			SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(ThemeRenderer.OnUserPreferenceChanged);
		}

		public ThemeRenderer(string className, int part, int state)
		{
			if( !ThemeRenderer.IsCombinationDefined(className, part) )
			{
				throw new ArgumentException( " Theme Invalid Combination!" );
			}
			this._class = className;
			this._part = part;
			this._state = state;
		}

		private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs ea)
		{
			if( ea.Category == (UserPreferenceCategory.Window | UserPreferenceCategory.Color) )
			{
				ThemeRenderer.globalCacheVersion += ((long)1);
			}
		}

		private static bool IsCombinationDefined(string className, int part)
		{
			bool flag = false;

			if( !ThemeRenderer.IsSupported )
			{
				if( !ThemeRenderer.IsEnabledByUser )
				{
					throw new InvalidOperationException( "Theme Not Active" );
				}
				throw new InvalidOperationException( "Them Disabled In Client Area" );
			}

			IntPtr ptr = ThemeRenderer.GetHandle(className, false);
			if( ptr == IntPtr.Zero )
			{
				return flag;
			}

			if( part == 0 )
			{
				return true;
			}

			return APITheme.IsThemePartDefined(ptr, part, 0);
		}

		public static bool IsCriticalException(Exception ex)
		{
			if( (!(ex is NullReferenceException) && !(ex is StackOverflowException)) && !(ex is OutOfMemoryException) )
			{
				return ((ex as ThreadAbortException) != null);
			}
			return true;
		}

		public Color GetColor(ThemeColorProperty prop)
		{
			if( !Enum.IsDefined(typeof(ThemeColorProperty), prop) )
			{
				throw new InvalidEnumArgumentException("prop", (int)prop, typeof(ThemeColorProperty));
			}

			uint pColor = 0;
			APITheme.GetThemeColor(this.Handle, this._part, this._state, (int)prop, out pColor);
			return ColorTranslator.FromWin32((int)pColor);
		}

		public Color GetThemeSysColor(ThemeColorProperty prop)
		{
			if( !Enum.IsDefined(typeof(ThemeColorProperty), prop) )
			{
				throw new InvalidEnumArgumentException("prop", (int)prop, typeof(ThemeColorProperty));
			}

			long win32Color = APITheme.GetThemeSysColor(this.Handle, (int)prop);
			return ColorTranslator.FromWin32((int) win32Color);
		}


		public IntPtr Handle
		{
			get
			{
				if( !ThemeRenderer.IsSupported )
				{
					if( !ThemeRenderer.IsEnabledByUser )
					{
						throw new InvalidOperationException( "Theme Not Active" );
					}
					throw new InvalidOperationException( "Theme Disabled In Client Area" );
				}
				return ThemeRenderer.GetHandle(this._class);
			}
		}

		private static IntPtr GetHandle(string className)
		{
			return ThemeRenderer.GetHandle(className, true);
		}

		private static IntPtr GetHandle(string className, bool throwExceptionOnFail)
		{
			ThemeHandle handle;

			if( ThemeRenderer.themeHandles == null )
			{
				ThemeRenderer.CreateThemeHandleHashtable();
			}
			if( ThemeRenderer.threadCacheVersion != ThemeRenderer.globalCacheVersion )
			{
				ThemeRenderer.RefreshCache();
				ThemeRenderer.threadCacheVersion = ThemeRenderer.globalCacheVersion;
			}

			if( !ThemeRenderer.themeHandles.Contains(className) )
			{
				handle = ThemeHandle.Create(className, throwExceptionOnFail);
				if( handle == null )
				{
					return IntPtr.Zero;
				}
				ThemeRenderer.themeHandles.Add(className, handle);
			}
			else
			{
				handle = ((ThemeHandle)ThemeRenderer.themeHandles[className]);
			}
			return handle.NativeHandle;
		}

		private static void CreateThemeHandleHashtable()
		{
			ThemeRenderer.themeHandles = new Hashtable(ThemeRenderer.numberOfPossibleClasses);
		}

		private static void RefreshCache()
		{
			string text;
			ThemeHandle handle = null;

			if( ThemeRenderer.themeHandles == null )
			{
				return;
			}

			string[] textArray = new string[ThemeRenderer.themeHandles.Keys.Count];

			ThemeRenderer.themeHandles.Keys.CopyTo(textArray, 0);

			for(int i = 0; (i < textArray.Length); ++i )
			{
				text = textArray[i];
				handle = ((ThemeHandle)ThemeRenderer.themeHandles[text]);
				
				if( handle != null )
				{
					handle.Dispose();
				}

				handle = ThemeHandle.Create(text, false);
				if( handle != null )
				{
					ThemeRenderer.themeHandles[text] = handle;
				}
			}
		}


		public static bool IsSupported
		{
			get
			{
				bool flag = false;
				IntPtr ptr = ThemeRenderer.GetHandle("BUTTON", false);
				flag = (ptr != IntPtr.Zero);
				return flag;
			}
		}

		public static bool IsEnabledByUser
		{
			get
			{
				if( !ThemeRenderer.IsSupportedByOS )
				{
					return false;
				}
				return APITheme.IsAppThemed();
			}
		}

		public static bool IsSupportedByOS
		{
			get
			{
				//return( OSFeature.Feature.IsPresent(OSFeature.Themes) );
				return APITheme.IsAppThemed();
			}
		}

		public void SetParameters(string className, int part, int state)
		{
			if( !ThemeRenderer.IsCombinationDefined(className, part) )
			{
				throw new ArgumentException( "Theme Invalid Combination");
			}

			this._class = className;
			this._part = part;
			this._state = state;
		}


		public void DrawBackground(Graphics g, Rectangle bounds)
		{
			if( g == null )
			{
				throw new ArgumentNullException();
			}

			APITheme.DrawThemeBackground( this.Handle, g.GetHdc(), this._part, this._state, new COMRECT(bounds), null);
		}

		public string Class
		{
			get{ return this._class; }
		}

		public int Part
		{
			get{ return this._part; }
		}

		public int State
		{
			get{ return this._state; }
		}

	}
}
